/*
	Copyright 2009 Jason Nottingham

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.


    How to submit a patch:
    1. create or modify a test case (under 'tests') so that it fails to compile/link/run without 
       your change
    2. verify you have the latest sources (resync/merge)
    3. fix or add the missing functionality
    4. verify that your changes fixes the newly broken test case in #1 - it now builds and runs 
       C++ the same as the original C#
    5. create a Subversion-friendly diff or patch file of your changes (including the test cases)
    6. submit your patch at http://gonative.codeplex.com


*/

#pragma once

class FileInfo
{
private:

	string name;

public:

	FileInfo( WCHAR *wc )
	{
		name.copy_from( wc );
	}
	FileInfo( string filename )
	{
		name.copy_from( filename );
	}

	FileInfo()
	{
	}

	FileInfo( const FileInfo &copy_from )
	{
		name.copy_from( copy_from.name );
	}

	~FileInfo()
	{
	}

	void copy_from( FileInfo other )
	{
		name.copy_from( other.name );
	}

	const WCHAR *Name()
	{
		return name.ToString();
	}

};



class Directory
{
public:
	static BOOL Exists( string name )
	{
		BOOL found = FALSE;

		WIN32_FIND_DATA fd;

		HANDLE hSearch = FindFirstFile( name.ToString(), &fd );
		if ( hSearch )
		{
			found = ( fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY );
			if ( FindNextFile( hSearch, &fd ) )
				found = FALSE;	// should only be one match
			FindClose( hSearch );
		}

		return found;
	}

	static BOOL CreateDirectory( string name )
	{
		return CreateDirectory( name.ToString() );
	}
};


class DirectoryInfo;

typedef List<FileInfo>	FileInfoList;
typedef List<DirectoryInfo>	DirectoryInfoList;

class DirectoryInfo
{
private:
	
	string name;
	WIN32_FIND_DATA fd;

public:

	DirectoryInfo( char *s )
	{
		WCHAR *wc = __goNative_DupAscii( s );
		name.copy_from( wc );
		free( wc );
	}

	DirectoryInfo( WCHAR *wc )
	{
		name.copy_from( wc );
	}

	DirectoryInfo()
	{
	}

	DirectoryInfo( const DirectoryInfo &copy_from )
	{
		name.copy_from( copy_from.name );
	}

	~DirectoryInfo()
	{
	}

	void copy_from( DirectoryInfo other )
	{
		name.copy_from( other.name );
	}

	DirectoryInfo( string directoryName )
	{
		name.copy_from( (WCHAR*)directoryName.ToString() );
	}

	List<FileInfo> *GetFiles( char *szMask )
	{
		WCHAR *wcMask = __goNative_DupAscii( szMask );
		FileInfoList *answer = GetFiles( wcMask );
		free( wcMask );
		return answer;
	}

	FileInfoList *GetFiles( WCHAR *wcFileMask )
	{
		FileInfoList *fi = new FileInfoList();

		WCHAR wcFullPath[512];
		StringCchPrintf( wcFullPath, 512, L"%s\\%s", name.ToString(), wcFileMask );

		HANDLE hSearch = FindFirstFile( wcFullPath, &fd );
		if ( hSearch )
		{
			do
			{
				if ( fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
				{
				}
				else
				{
					FileInfo newFileInfo( Path::Combine(name, fd.cFileName ) );
					fi->Add( newFileInfo );
				}
			} while( FindNextFile( hSearch, &fd ) );
			FindClose( hSearch );
		}
		
		return fi;
	}

	FileInfoList *GetFiles()
	{
		return GetFiles( L"*" );
	}

	DirectoryInfoList *GetDirectories()
	{
		return GetDirectories( L"*" );
	}

	List<DirectoryInfo> *GetDirectories( char *szMask )
	{
		WCHAR *wcMask = __goNative_DupAscii( szMask );
		DirectoryInfoList *answer = GetDirectories( wcMask );
		free( wcMask );
		return answer;
	}

	DirectoryInfoList *GetDirectories( WCHAR *wcDirectoryMask )
	{
		DirectoryInfoList *dirs = new DirectoryInfoList();

		WCHAR wcFullPath[512];
		StringCchPrintf( wcFullPath, 512, L"%s\\%s", name.ToString(), wcDirectoryMask );

		HANDLE hSearch = FindFirstFile( wcFullPath, &fd );
		if ( hSearch )
		{
			do
			{
				if ( fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
				{
					DirectoryInfo newDirInfo( Path::Combine( (WCHAR*)name.ToString(), fd.cFileName ) );
					dirs->Add( newDirInfo );
				}
				else
				{
				}
			} while( FindNextFile( hSearch, &fd ) );
			FindClose( hSearch );
		}
		
		return dirs;
	}

	const WCHAR *Name()
	{
		return name.ToString();
	}

	BOOL Exists()
	{
		return Directory::Exists( name );
	}

};

class File
{
private:
	static HANDLE openFile( const WCHAR *wcName, DWORD dwDesiredAccess )
	{
		return CreateFile( wcName, dwDesiredAccess, 0, NULL, dwDesiredAccess==GENERIC_READ ? OPEN_EXISTING : CREATE_ALWAYS, 0, NULL );
	}

	static BOOL writeStringBuffer( HANDLE h, const WCHAR *wc )
	{
		BOOL success = FALSE;
		CHAR *c = __goNative_DupWCHAR( wc );
		if ( c )
		{
			DWORD dwSize = strlen(c);
			DWORD dwBytesWritten;
			success = WriteFile( h, c, dwSize, &dwBytesWritten, NULL );
			if ( success )
				success = ( dwSize == dwBytesWritten );
		}
		return success;
	}

public:
	static BOOL Exists( string name )
	{
		HANDLE h = CreateFile( name.ToString(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
		BOOL exists = ( h != INVALID_HANDLE_VALUE );
		if ( exists )
			CloseHandle( h );
		return exists;
	}

	static BOOL Delete( string name )
	{
		return DeleteFile( name.ToString() );
	}

	static void WriteAllLines( string name, List<string> lines )
	{
		HANDLE h = openFile( name.ToString(), GENERIC_WRITE );
		if ( h != INVALID_HANDLE_VALUE )
		{
			string line;
			lines.start_iteration();
			while( lines.get_next( line ) )
				if ( !writeStringBuffer( h, line.ToString() ) )
					break;	// BUGBUG - handle/report failure?
			CloseHandle( h );
		}
	}

	static void WriteAllText( string name, string allText )
	{
		HANDLE h = openFile( name.ToString(), GENERIC_WRITE );
		if ( h != INVALID_HANDLE_VALUE )
		{
			writeStringBuffer( h, allText.ToString() );	// BUGBUG - handle/report failure?
			CloseHandle( h );
		}
	}

	static string ReadAllText( string name )
	{
		char *c = NULL;
		HANDLE h = openFile( name.ToString(), GENERIC_READ );
		if ( h != INVALID_HANDLE_VALUE )
		{
			DWORD dwSize = GetFileSize( h, NULL );
			DWORD dwBytesRead;
			c = (char*)malloc( dwSize + 1 );
			BOOL b = ReadFile( h, c, dwSize, &dwBytesRead, NULL );
			assert( b );
			assert( dwSize == dwBytesRead );
			if ( b && ( dwSize == dwBytesRead ) )
			{
				c[dwSize] = 0;
			}
			else
				free( c );
			CloseHandle( h );
		}

		string allText(c);
		free(c);
		return allText;
	}

    static List<string> *ReadAllLines( string name )
	{
		string allText = ReadAllText( name );
		string stripped = allText.Replace( "\r", "" );
		return stripped.Split( "\n" );
	}

};
